package cn.xinfei.xdecision.data.core.frame.strategy.virtual;

import cn.xinfei.xdecision.common.model.datax.collect.VirtualDataTransferReqVO;
import cn.xinfei.xdecision.common.model.datax.consts.VarPrometheusTagsConst;
import cn.xinfei.xdecision.common.model.datax.consts.VariableConst;
import cn.xinfei.xdecision.common.model.datax.enums.EnumUtil;
import cn.xinfei.xdecision.common.model.datax.enums.RetryStrategy;
import cn.xinfei.xdecision.common.model.datax.enums.VariableStrategyType;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import cn.xinfei.xdecision.data.core.util.JsonProcessor;
import cn.xinfei.xdecision.data.model.IVariableRequest;
import cn.xinfei.xdecision.data.model.IVariableResponse;
import cn.xinfei.xdecision.data.core.frame.retry.HttpClientManage;
import cn.xinfei.xdecision.data.core.frame.strategy.BaseStrategy;
import cn.xinfei.xdecision.data.core.frame.strategy.StrategyHandler;
import cn.xinfei.xdecision.data.core.vo.VarInterfaceInfoVO;
import cn.xinfei.xdecision.data.core.vo.virtual.VirtualVarRequest;
import cn.xinfei.xdecision.data.core.vo.virtual.VirtualVarResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.alibaba.fastjson.parser.Feature;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;

import java.net.SocketTimeoutException;
import java.util.*;

@StrategyHandler(type = VariableStrategyType.HTTP_VITUAL)
public class VirtualDataTransferStrategy extends BaseStrategy {

    private static final Logger LOGGER = LoggerFactory.getLogger(VirtualDataTransferStrategy.class);

    @Value("${xdecision.data.okhttp.maxIdle}")
    private int maxIdle;

    @Value("${xdecision.data.okhttp.keepAliveDuration}")
    private long keepAliveDuration;

    @Override
    public IVariableResponse process(IVariableRequest request) {
        Transaction catT = Cat.newTransaction("interface-datasource", "http-virtual");

        VirtualVarRequest virtualVarRequest = (VirtualVarRequest) request;
        checkStrategyInput(request);
        checkVirtualInterfaceInput(virtualVarRequest);

        VirtualVarResponse virtualVarResponse = new VirtualVarResponse();

        MDC.put(VariableConst.REQUEST_ID, request.requestId());
        if (!request.requestStrategys().contains(VariableStrategyType.HTTP_VITUAL)) {
            LOGGER.info("request = {} 没有包含 [Http_Virtual] request", JSONObject.toJSONString(request));
        } else {
            virtualVarResponse.setRequestId(virtualVarRequest.getRequestId());
            VarInterfaceInfoVO varInterfaceInfoVO = virtualVarRequest.getVarInterfaceInfoVO();
            String requestUrl = varInterfaceInfoVO.getUrl();
            RetryStrategy retryStrategy = EnumUtil.fromCodeOrThrow(RetryStrategy.class, varInterfaceInfoVO.getRetryStrategy());
            Integer retryNum = varInterfaceInfoVO.getRetryNum();

            String interfaceName = varInterfaceInfoVO.getName();
            Integer interfaceId = varInterfaceInfoVO.getId();
            if (StringUtils.isBlank(requestUrl)) {
                LOGGER.error("requestId = {} [Http_Virtual] requestUrl is null,please check interfaceId = {}  interfaceName = {} config",
                        virtualVarRequest.getRequestId(),
                        varInterfaceInfoVO.getId(),
                        varInterfaceInfoVO.getName());
                throw new XDecisionDataException(XDecisionDataExceptionType.DATA_INTERFACE_CONFIG_EMPTY_ERROR,
                        "请求[Virtual] DataTransfer url配置缺失! varCodes:{} " + JSONObject.toJSONString(virtualVarRequest.getVarCodes()));

            }
            long connectTimeout = varInterfaceInfoVO.getConnectTimeout();
            long readTimeout = varInterfaceInfoVO.getReadTimeout();
            long writeTimeout = varInterfaceInfoVO.getWriteTimeout();

            HttpClientManage okHttpClientManage = HttpClientManage.getOkHttpClientManageRetryable(
                    requestUrl, connectTimeout, readTimeout, writeTimeout, retryStrategy, retryNum, maxIdle, keepAliveDuration);
            Set<String> requestVarSets = new HashSet<>(virtualVarRequest.getVarCodes());
            Map<String, Object> varResult;

            JSONObject inputParams = virtualVarRequest.getInputParams();
            VirtualDataTransferReqVO virtualDataTransferReqVO;
            if (requestVarSets.size() > 0) {
                // 构造请求参数
                Map<String, String> rpcContextMap = buildVarContext(inputParams);
                int disableDecimalFeature = JSON.DEFAULT_PARSER_FEATURE & ~Feature.UseBigDecimal.getMask();
                virtualDataTransferReqVO = buildVirtualDataTransferReqVO(requestVarSets, JSONObject.parseObject(inputParams.toJSONString(), Map.class, disableDecimalFeature));
                // 发起请求
                long begin = System.currentTimeMillis();
                try {
                    String responseData = okHttpClientManage.postWithNotVerify(JsonProcessor.toJson(virtualDataTransferReqVO), HttpClientManage.MEDIATYPE_JSON, rpcContextMap);

                    if (StringUtils.isBlank(responseData)) {
                        throw new XDecisionDataException(XDecisionDataExceptionType.DATA_TRANSFER_RESPONSE_EMPTY, "请求[Virtual] DataTransfer结果为空! inputParam: " + inputParams.toJSONString());
                    }
                    long end = System.currentTimeMillis();
                    long virtualDuration = end - begin;
                    LOGGER.info("requestId = {}  请求[Virtual]变量 header = {}  contextMap = " + inputParams.toJSONString(),
                            virtualVarRequest.getRequestId(),
                            JSONObject.toJSONString(rpcContextMap));
                    LOGGER.info("requestId = {} [Virtual]变量获取时间 = " + virtualDuration + " MillSeconds", virtualVarRequest.getRequestId());
                    varResult = JSONObject.parseObject(responseData, Map.class);
                    LOGGER.info("requestId = {} 请求[Virtual]返回：variableResult = " + varResult, virtualVarRequest.getRequestId());
                    virtualVarResponse.setData(varResult);
                    catT.setStatus(Transaction.SUCCESS);
                    Metrics.counter(VarPrometheusTagsConst.VITUAL_DATASOURCE_SUCCESS_COUNT,
                            VarPrometheusTagsConst.MetricsFliedTags.REQUEST_URL, requestUrl,
                            VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_ID, interfaceId.toString(),
                            VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_NAME, interfaceName).increment();
                    return virtualVarResponse;
                } catch (SocketTimeoutException e) {
                    Cat.logError(e);
                    catT.setStatus(e);
                    LOGGER.error("requestId = {} 获取[Virtual] DataTransfer 抽象数据源变量连接超时异常, url = {}  varTransferRequest = {}",
                            virtualVarRequest.getRequestId(),
                            requestUrl,
                            JSONObject.toJSONString(virtualVarRequest),
                            e);
                    throw new XDecisionDataException(XDecisionDataExceptionType.DATA_TRANSER_SOCKER_TIMEOUT_ERROR,
                            "请求[Virtual] DataTransfer socket超时异常! varCodes:{} " + JSONObject.toJSONString(virtualVarRequest.getVarCodes()) + " url = " + requestUrl + " inputParam: " + inputParams.toJSONString(), e);
                } catch (Exception e) {
                    Cat.logError(e);
                    catT.setStatus(e);
                    LOGGER.error("requestId = {} 获取[Virtual] DataTransfer 抽象数据源变量异常, url = {} ,varTransferRequest = {}",
                            virtualVarRequest.getRequestId(),
                            requestUrl,
                            JSONObject.toJSONString(virtualVarRequest),
                            e);
                    Metrics.counter(VarPrometheusTagsConst.VITUAL_DATASOURCE_FAIL_COUNT,
                            VarPrometheusTagsConst.MetricsFliedTags.REQUEST_URL, requestUrl,
                            VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_ID, interfaceId.toString(),
                            VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_NAME, interfaceName).increment();
                    throw new XDecisionDataException(XDecisionDataExceptionType.DATA_TRANSFER_RESPONSE_ERROR,
                            "请求[Virtual] DataTransfer异常! varCodes = " + JSONObject.toJSONString(virtualVarRequest.getVarCodes()) + " url = " + requestUrl + " inputParam: " + inputParams.toJSONString(), e);
                } finally {
                    long end = System.currentTimeMillis();
                    catT.complete();
                    Metrics.counter(VarPrometheusTagsConst.VITUAL_DATASOURCEY_REQUEST_COUNT,
                            VarPrometheusTagsConst.MetricsFliedTags.REQUEST_URL, requestUrl,
                            VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_ID, interfaceId.toString(),
                            VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_NAME, interfaceName).increment();

                    Set<Tag> tagList = new HashSet<>();
                    tagList.add(Tag.of(VarPrometheusTagsConst.MetricsFliedTags.REQUEST_URL, requestUrl));
                    tagList.add(Tag.of(VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_ID, interfaceId.toString()));
                    tagList.add(Tag.of(VarPrometheusTagsConst.MetricsFliedTags.DATASOURCE_NAME, interfaceName));
                    Metrics.gauge(VarPrometheusTagsConst.VITUALD_DATASOURCE_DURATION, tagList, end - begin);
                }

            } else {
                LOGGER.info("requestId = {} 待请求的[Virtual]变量集合为空", virtualVarRequest.getRequestId());
            }
        }

        return virtualVarResponse;
    }


    private VirtualDataTransferReqVO buildVirtualDataTransferReqVO(Set<String> requestVarSet, Map inputParamMap) {
        VirtualDataTransferReqVO virtualDataTransferReqVO = new VirtualDataTransferReqVO();
        virtualDataTransferReqVO.setVarCodes(requestVarSet);
        virtualDataTransferReqVO.setContext(inputParamMap);
        return virtualDataTransferReqVO;
    }

    private Map<String, String> mergeContextMapFromInputParams(Map<String, String> contextMap, JSONObject inputParams) {
        String contextStr = JSONObject.toJSONString(contextMap);
        String inputParamsStr = inputParams.toJSONString();
        String mergeContextStr = contextStr + inputParamsStr;
        if (mergeContextStr.contains("}{")) {
            mergeContextStr = mergeContextStr.replace("}{", ",");
        }
        return JSON.parseObject(mergeContextStr, Map.class);

    }

}
